/*
 *   Copyright 2012, Thomas Kerber
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package milk.jpatch.access;

import milk.jpatch.CPoolMap;
import milk.jpatch.Patch;
import milk.jpatch.access.AccessFlagsPatch.AccessLevel;

/**
 * Denotes a patch of access rights.
 * @author Thomas Kerber
 * @version 1.0.0
 */
public abstract class AccessPatch implements Patch<AccessPatch.Access>{
    
    private static final long serialVersionUID = -1368464924230054679L;
    
    /**
     * The possible access rights:
     * 
     * public, protected, package, private.
     * @author Thomas Kerber
     * @version 1.0.0
     */
    public static enum Access{
        PUBLIC,
        PROTECTED,
        PACKAGE,
        PRIVATE;
        
        /**
         * Gets the bit flags for a certain access right:
         * 
         * 001 <-> public
         * 010 <-> private
         * 100 <-> protected
         * 000 <-> package
         * 
         * @return The bit flags for the access right.
         */
        public int getFlags(){
            if(this == Access.PUBLIC)
                return 0x1;
            if(this == Access.PRIVATE)
                return 0x2;
            if(this == Access.PROTECTED)
                return 0x4;
            return 0x0;
        }
    }
    
    /**
     * Derives the access right from given relevant flags.
     * @param relevantFlags The flags relevant to the access rights.
     *         For classes that means the last bit, for all others the three
     *         last bits.
     * @return The access rights denoted by the flags.
     */
    public static Access getAccess(int relevantFlags){
        switch(relevantFlags){
            case 0x0:
                return Access.PACKAGE;
            case 0x1:
                return Access.PUBLIC;
            case 0x2:
                return Access.PRIVATE;
            case 0x4:
                return Access.PROTECTED;
            default:
                throw new IllegalStateException(
                        "Not recognized access flags.");
        }
    }
    
    /**
     * Derives the access rights from given flags and an access level.
     * @param flags The flags.
     * @param a The access level.
     * @return The access rights.
     */
    public static Access getAccess(int flags, AccessLevel a){
        return getAccess(flags &
                ((1 << (a == AccessLevel.CLASS ? 1 : 3)) - 1));
    }
    
    /**
     * Generates an access rights patch.
     * @param old The old flags.
     * @param new_ The new flags.
     * @param a The access level.
     * @return The access rights patch.
     */
    public static AccessPatch generate(int old, int new_, AccessLevel a){
        int relevantDigits = a == AccessLevel.CLASS ? 1 : 3;
        // 2^x - 1 turns the last x bits on.
        int mask = (1 << relevantDigits) - 1;
        
        int relevantOld = old & mask;
        int relevantNew = new_ & mask;
        
        if(relevantOld == relevantNew)
            return new AccessID();
        else
            return new AccessReplace(getAccess(relevantNew));
    }
    
    /**
     * Convenience method without CPoolMap.
     * @param a The access rights to patch.
     * @return The patches access rights.
     */
    public abstract Access patch(Access a);
    
    @Override
    public final Access patch(Access a, CPoolMap map){
        return patch(a);
    }
    
}
